#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

// SM3 初始向量
const uint32_t SM3_IV[8] = {
    0x7380166F,
    0x4914B2B9,
    0x172442D7,
    0xDA8A0600,
    0xA96F30BC,
    0x163138AA,
    0xE38DEE4D,
    0xB0FB0E4E
};

// SM3 上下文结构体
typedef struct {
    uint32_t total[2];    // 消息长度，以位为单位
    uint32_t state[8];    // 哈希状态
    unsigned char buffer[64]; // 数据缓冲区
} sm3_context;

// 大端序读取4字节为一个32位无符号整数
#define GET_ULONG_BE(n,b,i)                             \
    do {                                                \
        (n) = ((uint32_t)(b)[(i)] << 24)             \
            | ((uint32_t)(b)[(i) + 1] << 16)         \
            | ((uint32_t)(b)[(i) + 2] << 8)          \
            | ((uint32_t)(b)[(i) + 3]);              \
    } while(0)

// 大端序写入32位无符号整数为4字节
#define PUT_ULONG_BE(n,b,i)                             \
    do {                                                \
        (b)[(i)]     = (unsigned char)((n) >> 24);    \
        (b)[(i) + 1] = (unsigned char)((n) >> 16);    \
        (b)[(i) + 2] = (unsigned char)((n) >> 8);     \
        (b)[(i) + 3] = (unsigned char)((n));          \
    } while(0)

// SM3 循环左移
#define ROTL(x,n) (((x) << (n)) | ((x) >> (32 - (n))))

// 定义布尔函数
#define FF0(x,y,z) ((x) ^ (y) ^ (z))
#define FF1(x,y,z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define GG0(x,y,z) ((x) ^ (y) ^ (z))
#define GG1(x,y,z) (((x) & (y)) | ((~(x)) & (z)))

// 非线性变换函数
#define P0(x) ((x) ^ ROTL((x),9) ^ ROTL((x),17))
#define P1(x) ((x) ^ ROTL((x),15) ^ ROTL((x),23))

// SM3 常量函数 Tj
#define Tj(j) ((j) <= 15 ? 0x79CC4519 : 0x7A879D8A)

// 填充常量
static const unsigned char sm3_padding[64] = {
    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// SM3 初始化
void sm3_starts(sm3_context *ctx) {
    ctx->total[0] = 0;
    ctx->total[1] = 0;
    memcpy(ctx->state, SM3_IV, sizeof(SM3_IV));
}

// SM3 处理一个64字节的数据块
static void sm3_process(sm3_context *ctx, const unsigned char data[64]) {
    uint32_t W[68], W1[64], A, B, C, D, E, F, G, H;
    uint32_t SS1, SS2, TT1, TT2;
    int j;

    // 消息扩展
    for (j = 0; j < 16; j++) {
        GET_ULONG_BE(W[j], data, j * 4);
    }
    for (j = 16; j < 68; j++) {
        W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROTL(W[j - 3], 15)) ^ ROTL(W[j - 13], 7) ^ W[j - 6];
    }
    for (j = 0; j < 64; j++) {
        W1[j] = W[j] ^ W[j + 4];
    }

    // 初始化寄存器
    A = ctx->state[0];
    B = ctx->state[1];
    C = ctx->state[2];
    D = ctx->state[3];
    E = ctx->state[4];
    F = ctx->state[5];
    G = ctx->state[6];
    H = ctx->state[7];

    // 压缩函数
    for (j = 0; j < 64; j++) {
        SS1 = ROTL((ROTL(A, 12) + E + ROTL(Tj(j), j)), 7);
        SS2 = SS1 ^ ROTL(A, 12);
        TT1 = (j <= 15 ? FF0(A, B, C) : FF1(A, B, C)) + D + SS2 + W1[j];
        TT2 = (j <= 15 ? GG0(E, F, G) : GG1(E, F, G)) + H + SS1 + W[j];
        D = C;
        C = ROTL(B, 9);
        B = A;
        A = TT1;
        H = G;
        G = ROTL(F, 19);
        F = E;
        E = P0(TT2);
    }

    // 更新状态
    ctx->state[0] ^= A;
    ctx->state[1] ^= B;
    ctx->state[2] ^= C;
    ctx->state[3] ^= D;
    ctx->state[4] ^= E;
    ctx->state[5] ^= F;
    ctx->state[6] ^= G;
    ctx->state[7] ^= H;
}

// SM3 更新函数
void sm3_update(sm3_context *ctx, const unsigned char *input, int ilen) {
    int fill;
    uint32_t left;

    if (ilen <= 0)
        return;

    left = ctx->total[0] & 0x3F;
    fill = 64 - left;

    ctx->total[0] += ilen;
    if (ctx->total[0] < (uint32_t)ilen)
        ctx->total[1]++;

    if (left && ilen >= fill) {
        memcpy(ctx->buffer + left, input, fill);
        sm3_process(ctx, ctx->buffer);
        input += fill;
        ilen -= fill;
        left = 0;
    }

    while (ilen >= 64) {
        sm3_process(ctx, input);
        input += 64;
        ilen -= 64;
    }

    if (ilen > 0) {
        memcpy(ctx->buffer + left, input, ilen);
    }
}

// SM3 完成并输出哈希值
void sm3_finish(sm3_context *ctx, unsigned char output[32]) {
    uint32_t high, low;
    uint32_t last, padn;
    unsigned char msglen[8];

    high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
    low = (ctx->total[0] << 3);

    PUT_ULONG_BE(high, msglen, 0);
    PUT_ULONG_BE(low, msglen, 4);

    last = ctx->total[0] & 0x3F;
    padn = (last < 56) ? (56 - last) : (120 - last);

    sm3_update(ctx, sm3_padding, padn);
    sm3_update(ctx, msglen, 8);

    PUT_ULONG_BE(ctx->state[0], output, 0);
    PUT_ULONG_BE(ctx->state[1], output, 4);
    PUT_ULONG_BE(ctx->state[2], output, 8);
    PUT_ULONG_BE(ctx->state[3], output, 12);
    PUT_ULONG_BE(ctx->state[4], output, 16);
    PUT_ULONG_BE(ctx->state[5], output, 20);
    PUT_ULONG_BE(ctx->state[6], output, 24);
    PUT_ULONG_BE(ctx->state[7], output, 28);
}

// 单次调用 SM3 算法
void sm3(const unsigned char *input, int ilen, unsigned char output[32]) {
    sm3_context ctx;

    sm3_starts(&ctx);
    sm3_update(&ctx, input, ilen);
    sm3_finish(&ctx, output);

    memset(&ctx, 0, sizeof(sm3_context)); // 清零上下文
}

// 打印缓冲区为十六进制
void dumpbuf(const unsigned char *buf, int len) {
    for (int i = 0; i < len; i++) {
        printf("%02x", buf[i]);
    }
    printf("\n");
}

// 测试标准测试向量
void test_standard_vectors() {
    printf("=== SM3 标准测试向量测试 ===\n");
    
    // 测试向量1: "abc"
    const unsigned char test1[] = "abc";
    unsigned char hash1[32];
    sm3(test1, strlen((const char*)test1), hash1);
    printf("测试1 (abc):\n");
    printf("期望: 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0\n");
    printf("实际: ");
    dumpbuf(hash1, 32);
    
    // 测试向量2: "abcd" * 16
    const unsigned char test2[] = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
    unsigned char hash2[32];
    sm3(test2, strlen((const char*)test2), hash2);
    printf("\n测试2 (abcd*16):\n");
    printf("期望: debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732\n");
    printf("实际: ");
    dumpbuf(hash2, 32);
}

// 主函数
int main(void) {
    // 测试用户输入
    char input[1024];
    printf("请输入要计算SM3哈希的消息: ");
    fgets(input, sizeof(input), stdin);
    
    // 去除换行符
    input[strcspn(input, "\n")] = 0;
    
    unsigned char hash[32];
    sm3((const unsigned char*)input, strlen(input), hash);
    
    printf("消息: %s\n", input);
    printf("SM3哈希结果: ");
    dumpbuf(hash, 32);
    printf("\n");
    
    // 运行标准测试
    test_standard_vectors();
    
    return 0;
}
